/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    image_header.c
 *  @brief   image header verify
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#include "crc.h"
#include "image_header.h"
#include "printf.h"

static int32_t magic_number_check(uint32_t magic_number)
{
	if (OCUBES_MAGIC != magic_number) {
		printf("magic_number_check failed, magic_s = 0x%lx, magic_d = 0x%x\r\n", magic_number, OCUBES_MAGIC);
		return -1;
	}

	return 0;
}

static int32_t board_type_check(uint32_t board_type)
{
	return 0;
}

static int32_t soc_type_check(uint32_t soc_type)
{
	if (eSOC_EOCV100 != soc_type) {
		printf("soc_type_check failed, board_type = 0x%lx\r\n", soc_type);
		return -1;
	}

	return 0;
}

static int32_t hdr_size_check(uint32_t hdr_size)
{
	uint32_t size = sizeof(struct pkg_header);

	if (size % 256 != 0)
		size = (1 + size/256) * 256;

	if (hdr_size != size) {
		printf("hdr_size_check failed, size = %lu, hdr_size = %lu\r\n", size, hdr_size);
		return -1;
	}

	return 0;
}

static int32_t hdr_crc_check(struct pkg_header *pkg_hdr)
{
	uint32_t dcrc = 0;
	uint32_t size = sizeof(struct pkg_header);
	uint32_t offset1 = 0;
	uint32_t offset2 = 0;
	uint32_t tmp = 0;
	uint8_t data[4] = {0};

	if (size % 256 != 0)
		size = (1 + size/256) * 256;

	offset1 = offsetof(struct pkg_header, hdr_crc32);
	offset2 = offsetof(struct pkg_header, boot_partition);

	if (offset1 > offset2) {
		tmp = offset1;
		offset1 = offset2;
		offset2 = tmp;
	}

	dcrc = crc32(0, (uint8_t *)pkg_hdr, offset1);
	dcrc = crc32(dcrc, data, 4);
	dcrc = crc32(dcrc, (uint8_t *)pkg_hdr + offset1 + 4, offset2 - offset1 - 4);
	dcrc = crc32(dcrc, data, 4);
	dcrc = crc32(dcrc, (uint8_t *)pkg_hdr + offset2 + 4, size - offset2 - 4);
	if (ntohl(pkg_hdr->hdr_crc32) != dcrc) {
		printf("hdr_crc_check failed, s_crc = 0x%lx, dcrc = 0x%lx\r\n", ntohl(pkg_hdr->hdr_crc32), dcrc);
		return -1;
	}

	return 0;
}

static bool is_little(void) {
	short a = 0x0001;
	return *(char *)&a == 0x01;
}

uint32_t ntohl(uint32_t a) {
	uint32_t b = 0;
	uint32_t c = a;
	int i = 0;

	if (!is_little())
		return c;

	for (i = 0; i < sizeof (c); i++) {
		b = (b << 8) | (c & 0xff);
		c >>= 8;
	}

	return b;
}

uint32_t htonl(uint32_t a) {
	uint32_t b = 0;
	uint32_t c = a;
	int i = 0;

	if (!is_little())
		return c;

	for (i = 0; i < sizeof (c); i++) {
		b = (b << 8) | (c & 0xff);
		c >>= 8;
	}

	return b;
}

int subimg_data_check(uint8_t *data_addr, uint32_t data_size, uint32_t data_crc32)
{
	uint32_t dcrc = 0;

	dcrc = crc32(0, data_addr, data_size);

	if (data_crc32 != dcrc) {
		printf("subimg_data_check failed, data_addr = 0x%lx, data_size = 0x%lx, data_crc = 0x%lx, dcrc = 0x%lx\r\n",
		       (uint32_t)data_addr, data_size, data_crc32, dcrc);
		return -1;
	}

	return 0;
}

int pkg_header_check(struct pkg_header *hdr)
{
	if (magic_number_check(ntohl(hdr->magic)))
		return -1;

	if (hdr_size_check(ntohl(hdr->hdr_size)))
		return -1;

	if (hdr_crc_check(hdr))
		return -1;

	if (soc_type_check(ntohl(hdr->soc_type)))
		return -1;

	if (board_type_check(ntohl(hdr->board_type)))
		return -1;

	return 0;
}

